<?php

/**
 * @category  Arc90_Service_Yammer
 * @package   Arc90_Service_Yammer
 * @author    Matt Williams <matt@mattwilliamsnyc.com>
 * @copyright Copyright (c) 2008 {@link http://arc90.com Arc90 Inc.}
 * @license   http://www.opensource.org/licenses/bsd-license.php
 */

/**
 * Modification by Doug Burns <dougb@arc90.com> 1/22/09
 * - Set User-Agent to Arc90_Service_Twitter for all request because
 *   Yammer was throwing an error without it.
 */

/**
 * Software License Agreement (BSD License)
 * 
 * Copyright (c) 2008, Arc90 Inc.
 * All rights reserved.
 * 
 * Redistribution and use of this software in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 * 
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * 
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * - Neither the name of Arc90 Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission of Arc90 Inc.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * Arc90_Service_Yammer provides methods for interacting with the {@link http://www.yammer.com Yammer} API (v1).
 *
 * Based on Yammer's {@link http://www.yammer.com/company/api_doc online documentation}.
 * 
 * Note: A strict rate limit of 5 requests per minute is applied to <i>all</i> API requests, so clients should never
 * poll for new messages more frequently than every 30 seconds to ensure that the user is still able to use the API
 * for posting messages, etc. Exceeding this limit will result in all resources returning a status code of 403
 * (Forbidden).
 *
 * @category  Arc90_Service_Yammer
 * @package   Arc90_Service_Yammer
 * @author    Matt Williams <matt@mattwilliamsnyc.com>
 * @copyright Copyright (c) 2008 {@link http://arc90.com Arc90 Inc.}
 * @license   http://www.opensource.org/licenses/bsd-license.php
 */
class Arc90_Service_Yammer
{
    /**
     * Entry point for the Yammer REST API.
     */
    const API_URI = 'https://yammer.com/api/v1';
	
	// Request parameters
	const USER_AGENT = 'Arc90_Service_Yammer';

    // Messages
    const PATH_MSG_BASE      = '/messages';
    const PATH_MSG_FOLLOWING = '/messages/following';
    const PATH_MSG_FROM_USER = '/messages/from_user';
    const PATH_MSG_IN_THREAD = '/messages/in_thread';
    const PATH_MSG_RECEIVED  = '/messages/received';
    const PATH_MSG_SENT      = '/messages/sent';
    const PATH_MSG_TAGGED    = '/messages/tagged_with';

    // Tags
    const PATH_TAGS_BASE     = '/tags';

    // Users
    const PATH_USERS_BASE    = '/users';
    const PATH_USERS_CURRENT = '/users/current';

    // Subscriptions
    const PATH_SUBS_BASE     = '/subscriptions';
    const PATH_SUBS_TO_TAG   = '/subscriptions/to_tag';
    const PATH_SUBS_TO_USER  = '/subscriptions/to_user';

    /**
     * Yammer account username.
     * @var    string
     * @access private
     */
    protected $_authUsername = '';

    /**
     * Yammer account password.
     * @var    string
     * @access private
     */
    protected $_authPassword = '';

    /**
     * Yammer client name.
     * @var    string
     * @access private
     */
    protected $_authClientName = '';

    /**
     * Yammer client password.
     * @var    string
     * @access private
     */
    protected $_authClientKey = '';

    /**
     * Length of time (in seconds) to wait for a respnse from Yammer before timing out.
     * @var    integer
     * @access private
     */
    protected $_timeout = 10;

    /**
     * Constructs a new Yammer Web Service Client.
     *
     * @param string  $username   Yammer account username
     * @param string  $password   Yammer account password
     * @param integer $timeout    Length of time (in seconds) before timeout
     * @param string  $clientName Yammer-approved client application name
     * @param string  $clientKey  Yammer-approved client application key
     */
    public function __construct($username ='', $password ='', $timeout =10, $clientName ='', $clientKey ='')
    {
        $this->setAuth($username, $password)
             ->setClient($clientName, $clientKey)
             ->setTimeout($timeout);
    }

    /**
     * Sets username and password of the authenticating user.
     *
     * Provides a fluent interface.
     *
     * @param string $username Yammer account username
     * @param string $password Yammer account password
     *
     * @return Arc90_Service_Yammer
     */
    public function setAuth($username, $password)
    {
        $this->_authUsername = $username;
        $this->_authPassword = $password;

        return $this;
    }

    /**
     * Sets Yammer-approved client application data.
     *
     * Provides a fluent interface.
     *
     * @param string $clientName Yammer client name
     * @param string $clientKey  Yammer client key
     *
     * @return Arc90_Service_Yammer
     */
    public function setClient($clientName, $clientKey)
    {
        $this->_authClientName = $clientName;
        $this->_authClientKey  = $clientKey;

        return $this;
    }

    /**
     * Sets the length of time (in seconds) to wait for a respnse from Yammer before timing out.
     *
     * Provides a fluent interface.
     *
     * @param integer $timeout Length of time (in seconds) before timeout
     *
     * @return Arc90_Service_Yammer
     * @throws Arc90_Service_Yammer_Exception
     */
    public function setTimeout($timeout)
    {
        // Verify that the timeout parameter is a positive integer
        self::_validatePositiveInteger('timeout', $timeout);

        $this->_timeout = $timeout;

        return $this;
    }

    /**
     * Deletes a message owned by the current user.
     *
     * @param integer $id ID of the message to be deleted
     *
     * @return Arc90_Service_Yammer
     * @throws Arc90_Service_Yammer_Exception
     */
    public function deleteMessage($id)
    {
        // Verify that the message ID is a positive integer
        self::_validatePositiveInteger('id', $id);

        // Build URI for this request
        $uri = sprintf('%s/%s', self::PATH_MSG_BASE, $id);

        // Send request and return response
        return $this->_sendRequest($uri, 'DELETE');
    }

    /**
     * Retrieves all messages in this network. Corresponds to the "All" tab on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesAll($format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_BASE, $format, $params, array('newer_than', 'older_than'));
    }

    /**
     * Retrieves messages followed by the logged-in user.
     *
     * Corresponds to the "Following" tab on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesFollowing($format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_FOLLOWING, $format, $params, array('newer_than', 'older_than'));
    }

    /**
     * Retrieves messages sent by the user with the given ID.
     *
     * Corresponds to the messages on a user profile page on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $id     ID of the user whose messages are being retrieved
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesFromUser($id, $format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_FROM_USER, $format, $params, array('newer_than', 'older_than'), $id);
    }

    /**
     * Retrieves messages in the thread with the given ID.
     *
     * Corresponds to the page you'd see when clicking on "in reply to" on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param integer $id     ID of the thread for which messages are being retrieved
     * @param string  $format Desired response format (defaults to JSON)
     * @param array   $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesInThread($id, $format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_IN_THREAD, $format, $params, array('newer_than', 'older_than'), $id);
    }

    /**
     * Retrieves messages received by the logged-in user.
     *
     * Corresponds to the "Received" tab on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesReceived($format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_RECEIVED, $format, $params, array('newer_than', 'older_than'));
    }

    /**
     * Retrieves messages sent by the authenticating user
     * (Alias for <i>/messages/from_user/logged-in_user_id.format</i>).
     *
     * Corresponds to the "Sent" tab on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesSent($format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_SENT, $format, $params, array('newer_than', 'older_than'));
    }

    /**
     * Retrieves messages including the tag with given ID.
     *
     * Corresponds to the messages on a tag's page on the website.
     *
     * Optional Query Parameters:
     *
     * <ul>
     *  <li>
     *    <b>older_than</b>:
     *    Returns only messages older than the message ID specified. This is useful for paginating messages.
     *    For example, if you're currently viewing 20 messages and the oldest is number 2912, you could append
     *    "?older_than=2912" to your HTTP request to get the 20 messages prior to those you're seeeing. 
     *  </li>
     *  <li>
     *    <b>newer_than</b>:
     *    Returns only messages newer than the message ID specified. This should <b>always</b> be used when polling for
     *    new messages. If you're looking at a message resource, and the most recent message returned is 3516, you
     *    could make requests with the parameter "?newer_than=3516" to ensure that you do not get duplicate copies of
     *    messages already on your page. 
     *  </li>
     * </ul>
     * 
     * Formats: xml, json
     *
     * @param string $id     ID of the tag for which messages are being retrieved
     * @param string $format Desired response format (defaults to JSON)
     * @param array  $params See method documentation for valid query parameters
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getMessagesTaggedWith($id, $format ='json', array $params =array())
    {
        return $this->_getResource(self::PATH_MSG_TAGGED, $format, $params, array('newer_than', 'older_than'), $id);
    }

    /**
     * Checks to see if you are subscribed to the tag of the given id.
     *
     * Formats: xml, json
     *
     * @param string $user   Tag for which subscription status is being checked
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getSubscriptionToTag($id, $format ='json')
    {
        return $this->_getResource(self::PATH_SUBS_TO_TAG, $format, array(), array(), $id);
    }

    /**
     * Checks to see if you are subscribed to the user of the given id.
     *
     * Formats: xml, json
     *
     * @param string $user   User for whom subscription status is being checked
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getSubscriptionToUser($id, $format ='json')
    {
        return $this->_getResource(self::PATH_SUBS_TO_USER, $format, array(), array(), $id);
    }

    /**
     * Retrieves data about one tag.
     *
     * NOTE: At the time of this writing, this feature is documented, but NOT IMPLEMENTED YET.
     *
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getTag($id, $format ='json')
    {
        return $this->_getResource(self::PATH_TAGS_BASE, $format, array(), array(), $id);
    }

    /**
     * Retrieves all tags in this network.
     *
     * NOTE: At the time of this writing, this feature is documented, but NOT IMPLEMENTED YET.
     *
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getTags($format ='json')
    {
        return $this->_getResource(self::PATH_TAGS_BASE, $format);
    }


    /**
     * Retrieves data about one user.
     *
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getUser($id, $format ='json')
    {
        return $this->_getResource(self::PATH_USERS_BASE, $format, array(), array(), $id);
    }

    /**
     * Retrieves data about the authenticating user.
     *
     * Formats: xml, json
     *
     * @param string $format Desired response format (defaults to JSON)
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getUserCurrent($format ='json')
    {
        return $this->_getResource(self::PATH_USERS_CURRENT, $format);
    }

    /**
     * Retrieves all users in this network.
     *
     * Formats: xml, json
     *
     * @param string  $format Desired response format (defaults to JSON)
     * @param integer $page   20 results will be shown per page
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function getUsers($format ='json', $page)
    {
        return $this->_getResource(self::PATH_USERS_BASE, $format, array('page' => $page));
    }

    /**
     * Creates a new message.
     *
     * @param string  $message       The text of the message body
     * @param integer $replied_to_id The message ID this message is in reply to, if applicable
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function postMessage($message, $replied_to_id =0)
    {
		$uri = self::PATH_MSG_BASE;
	
        // Encode message to be posted
        $data = 'body=' . urlencode($message);

        // If this is a reply to a previous message, validate and post original message ID
        if(0 != $replied_to_id)
        {
            self::_validatePositiveInteger('replied_to_id', $replied_to_id);

            $data .= '&replied_to_id=' . $replied_to_id;
        }

        // Send request and return response
        return $this->_sendRequest($uri, 'POST', $data);
    }

    /**
     * Subscribes to a user or tag.
     *
     * @param string $target_type The target type (<b>user</b> or <b>tag</b>) to which you are subscribing
     * @param string $target_id   The target ID to which you are subscribing
     *
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function subscribe($target_type, $target_id)
    {
        // Check requested response format and throw an exception if invalid
        self::_validateOption($target_type =strtolower($target_type), array('user', 'tag'));

        // Build URI for this request
        $uri = self::PATH_SUBS_BASE;

        // Set POST data for new subscription
        $data = "target_type={$target_type}&target_id={$target_id}";

        // Send request and return response
        return $this->_sendRequest($uri, 'POST', $data);
    }

    /**
     * Unsubscribes to a user or tag.
     *
     * @param string $target_type The target type (<b>user</b> or <b>tag</b>) to which you are unsubscribing
     * @param string $target_id   The target ID to which you are unsubscribing
     *
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     */
    public function unsubscribe($target_type, $target_id)
    {
        // Check requested response format and throw an exception if invalid
        self::_validateOption($target_type =strtolower($target_type), array('user', 'tag'));

        // Build URI for this request, including parameters for subscription cancellation
        $uri = self::PATH_SUBS_BASE . "?target_type={$target_type}&target_id={$target_id}";

        // Send request and return response
        return $this->_sendRequest($uri, 'DELETE');
    }

    /**
     * Retrieves a collection or individual item resource.
     *
     * @param string $uri     URI of the resource to be retrieved
     * @param string $format  Desired response format (defaults to JSON)
     * @param array  $params  User-supplied query parameters
     * @param array  $allowed Allowed user-supplied parameter keys for this request
     * @param string $id      Optional ID associated with a resource
     * 
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected function _getResource($uri, $format, array $params =array(), array $allowed =array(), $id ='')
    {
        // Check requested response format and throw an exception if invalid
        self::_validateOption($format =strtolower($format), array('json', 'xml'));

        // Build URI for this request, formatting for resource IDs when appropriate
        $uri = ('' == $id) ? sprintf('%s.%s', $uri, $format) : sprintf('%s/%s.%s', $uri, $id, $format); 

        // Append optional query string to request URI
        $uri .= self::_buildQueryString($params, $allowed);

        // Send request and return response
        return $this->_sendRequest($uri);
    }

    /**
     * Sends an HTTP request to the Yammer API using HTTP Basic user authentication.
     *
     * @param string  $uri    Target URI for this request (relative to the API root)
     * @param boolean $method Specifies the HTTP method to be used for this request
     * @param string  $data   x-www-form-urlencoded data to be sent in a POST request body
     *
     * @return Arc90_Service_Yammer_Response
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected function _sendRequest($uri, $method ='GET', $data ='')
    {
        // Open a cURL resource handle
        $ch = curl_init();

        // Assign the target URI for this HTTP request
        curl_setopt($ch, CURLOPT_URL, self::API_URI . $uri);

        // Return response body as a string rather than outputting directly
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        // Set HTTP Basic authentication credentials (Base-64 encoded)
        curl_setopt($ch, CURLOPT_USERPWD, "{$this->_authUsername}:{$this->_authPassword}");

        // Do not verify Yammer's SSL certificate
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

        // If this is a POST request...
        if('POST' == ($method = strtoupper($method)))
        {
            // Set HTTP method to POST
            curl_setopt($ch, CURLOPT_POST, TRUE);

            // Set data for request body
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }

        // If this is not a GET request (the default)...
        else if('GET' != $method)
        {
            // Set custom HTTP request method
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        }

        // Set a timeout value for this request
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, TRUE);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);

		// Set default headers
		$headers = array();
		$headers[] = 'User-Agent: ' . self::USER_AGENT;

        // If Client Authentication is being used, set appropriate headers
        if('' != $this->_authClientName)
        {
            // Generate Nonce and Hexdigest header values
            $nonce  = sprintf('%s:%s', mt_rand(), time());
            $digest = bin2hex(sha1(sprintf('%s:%s', $nonce, $this->_authClientKey)));

            // Set Yammer client X- headers
            $headers[] = "X-Yammer-Client: {$this->_authClientName}";
            $headers[] = "X-Yammer-Client-Nonce: {$nonce}";
            $headers[] = "X-Yammer-Client-Hexdigest: {$digest}";
        }

		// Add all headers to the request
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 

        // Execute HTTP request and capture response data
        $data = curl_exec($ch);

        // Capture metadata about the request
        $meta = curl_getinfo($ch);

        // Close the cURL handle
        curl_close($ch);

        /**
         * @see Arc90_Service_Yammer_Response
         */
        require_once('Arc90/Service/Yammer/Response.php');

        // Create and return a new response object from cURL info
        return new Arc90_Service_Yammer_Response($data, $meta);
    }

    /**
     * Builds a query string from an array of parameters and values.
     *
     * @param array $args    Key/value pairs to be evaluated for this query string
     * @param array $allowed Optional array of allowed parameter keys
     *
     * @return string
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected static function _buildQueryString(array $args, array $allowed =array())
    {
        // If allowed parameters are defined for this query...
        if(count($allowed))
        {
            // If disallowed parameters are present, throw an exception with an error message
            if(count($disallowed = array_diff(array_keys($args), $allowed)))
            {
                self::_throwException('Unknown or unsupported parameter(s): ' . implode(',', $disallowed));
            }
        }

        // Begin with empty query string parameters
        $params = array();

        // Process argument array
        foreach($args as $key => $value)
        {
            // Only process non-null parameters
            if(Null != $value)
            {
                switch($key)
                {
                    // Validate and format ID parameters
                    case 'newer_than':
                    case 'older_than':
                    case 'page':
                    {
                        $params[] = self::_buildQueryStringPositiveInt($key, $value);

                        break;
                    }
                    default:
                    {
                        self::_throwException("Unknown or unsupported query parameter '{$key}'");
                    }
                }
            }
        }

        // Return a formatted query string - empty if all arguments are null
        return count($params) ? '?' . implode('&', $params) : '';
    }

    /**
     * Validates that a parameter is a positive integer and formats a partial query string for Yammer API requests.
     *
     * @param string  $name  Name of the parameter being validated and formatted
     * @param integer $value Value of the parameter being validated and formatted
     *
     * @return string
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected static function _buildQueryStringPositiveInt($name, $value)
    {
        // Must be an integer greater than zero.
        self::_validatePositiveInteger($name, $value);

        return "{$name}={$value}";
    }

    /**
     * Throws an Arc90_Service_Yammer_Exception.
     *
     * @param string  $message Message to be associated with the exception
     * @param integer $code    Code to be associated with the exception
     *
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected static function _throwException($message, $code =1)
    {
        /**
         * @see Arc90_Service_Yammer_Exception
         */
        require_once 'Arc90/Service/Yammer/Exception.php';

        // Throw an exception with the supplied info
        throw new Arc90_Service_Yammer_Exception($message, $code);
    }

    /**
     * Validates an option against a set of allowed options.
     *
     * Provides a fluent interface.
     *
     * @param mixed $option  Option to validate
     * @param array $options Array of allowed option values
     *
     * @return Arc90_Service_Yammer
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected static function _validateOption($option, array $options)
    {
        if(!in_array($option, $options))
        {
            self::_throwException("Invalid option '{$option}'. Valid options include: " . implode(', ', $options));
        }

        return $this;
    }

    /**
     * Verifies that a given value is either a positive integer or a string representing a positive integer.
     *
     * Provides a fluent interface.
     *
     * @param string $name  Name of the field being verified
     * @param mixed  $value Value of the field being verified
     *
     * @return Arc90_Service_Yammer
     * @throws Arc90_Service_Yammer_Exception
     * @access private
     */
    protected static function _validatePositiveInteger($name, $value)
    {
        // Allows integer values or string values that represent valid positive integers
        if(0 >= $value || !(is_integer($value) || (is_numeric($value) && floatval($value) == intval(floatval($value)))))
        {
            self::_throwException("Field '{$name}' must be a positive integer value, '{$value}' given");
        }

        return $this;
    }
}
